#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
内存缓存服务

实现基于内存的高性能数据缓存系统，用于TCP迁移方案。
提供实时数据存储、版本控制、时间窗口管理等功能。
"""

import time
import threading
from datetime import datetime, timezone
from typing import Dict, List, Optional, Any
from collections import deque
from threading import RLock
import logging
import psutil
from dataclasses import dataclass, asdict

logger = logging.getLogger(__name__)


@dataclass
class CacheMetrics:
    """缓存性能指标"""
    cache_hits: int = 0
    cache_misses: int = 0
    data_updates: int = 0
    cleanup_runs: int = 0
    total_requests: int = 0
    avg_response_time: float = 0.0
    
    @property
    def hit_rate(self) -> float:
        """缓存命中率"""
        total = self.cache_hits + self.cache_misses
        return self.cache_hits / total if total > 0 else 0.0


@dataclass
class MemoryUsage:
    """内存使用情况"""
    rss_mb: float  # 物理内存使用量(MB)
    vms_mb: float  # 虚拟内存使用量(MB)
    percent: float  # 内存使用百分比
    cache_size: int  # 缓存中的房间数量


class MemoryCacheService:
    """内存缓存服务
    
    提供高性能的内存数据缓存，支持：
    - 最新数据缓存
    - 时间窗口数据
    - 版本控制
    - 自动过期清理
    - 并发安全
    - 性能监控
    """
    
    def __init__(self, 
                 max_rooms: int = 100,
                 max_window_size: int = 300,
                 cache_ttl: int = 3600,
                 cleanup_interval: int = 300):
        """
        初始化内存缓存服务
        
        Args:
            max_rooms: 最大房间数量
            max_window_size: 时间窗口最大数据条数
            cache_ttl: 缓存TTL(秒)
            cleanup_interval: 清理间隔(秒)
        """
        self._lock = RLock()
        
        # 缓存数据结构
        self.latest_data: Dict[str, Dict] = {}  # {room_id: latest_data}
        self.data_versions: Dict[str, int] = {}  # {room_id: version}
        self.time_window_data: Dict[str, deque] = {}  # {room_id: deque([(timestamp, data)])}
        self.cache_expiry: Dict[str, float] = {}  # {room_id: expire_timestamp}
        
        # 配置参数
        self.max_rooms = max_rooms
        self.max_window_size = max_window_size
        self.cache_ttl = cache_ttl
        self.cleanup_interval = cleanup_interval
        
        # 性能指标
        self.metrics = CacheMetrics()
        
        # 启动后台清理任务
        self._cleanup_timer = None
        self._start_cleanup_timer()
        
        logger.info(f"内存缓存服务已启动 - 最大房间数: {max_rooms}, 窗口大小: {max_window_size}, TTL: {cache_ttl}s")
    
    def add_data(self, room_id: str, data: Dict) -> Dict[str, Any]:
        """添加数据到缓存
        
        Args:
            room_id: 房间ID
            data: 数据内容
            
        Returns:
            包含版本号和时间戳的响应
        """
        start_time = time.time()
        
        with self._lock:
            current_time = time.time()
            
            # 检查房间数量限制
            if len(self.latest_data) >= self.max_rooms and room_id not in self.latest_data:
                self._evict_oldest_room()
            
            # 更新最新数据
            self.latest_data[room_id] = data
            
            # 递增版本号
            self.data_versions[room_id] = self.data_versions.get(room_id, 0) + 1
            
            # 添加到时间窗口
            if room_id not in self.time_window_data:
                self.time_window_data[room_id] = deque()
            
            self.time_window_data[room_id].append((current_time, data))
            
            # 限制时间窗口大小
            while len(self.time_window_data[room_id]) > self.max_window_size:
                self.time_window_data[room_id].popleft()
            
            # 更新过期时间
            self.cache_expiry[room_id] = current_time + self.cache_ttl
            
            # 更新指标
            self.metrics.data_updates += 1
            
            # 记录响应时间
            response_time = time.time() - start_time
            self._update_avg_response_time(response_time)
            
            result = {
                'version': self.data_versions[room_id],
                'timestamp': current_time,
                'cache_size': len(self.latest_data)
            }
            
            logger.debug(f"数据已添加到缓存 - 房间: {room_id}, 版本: {result['version']}")
            return result
    
    def get_latest_data(self, room_id: str) -> Optional[Dict]:
        """获取最新数据
        
        Args:
            room_id: 房间ID
            
        Returns:
            最新数据或None
        """
        start_time = time.time()
        
        with self._lock:
            self.metrics.total_requests += 1
            
            if room_id in self.latest_data:
                self.metrics.cache_hits += 1
                result = self.latest_data[room_id]
                logger.debug(f"缓存命中 - 房间: {room_id}")
            else:
                self.metrics.cache_misses += 1
                result = None
                logger.debug(f"缓存未命中 - 房间: {room_id}")
            
            # 记录响应时间
            response_time = time.time() - start_time
            self._update_avg_response_time(response_time)
            
            return result
    
    def get_data_since_timestamp(self, room_id: str, since_timestamp: float, limit: Optional[int] = None) -> List[Dict]:
        """获取指定时间戳之后的数据
        
        Args:
            room_id: 房间ID
            since_timestamp: 起始时间戳
            limit: 最大返回条数
            
        Returns:
            数据列表
        """
        start_time = time.time()
        
        with self._lock:
            self.metrics.total_requests += 1
            
            if room_id not in self.time_window_data:
                self.metrics.cache_misses += 1
                logger.debug(f"时间窗口数据未找到 - 房间: {room_id}")
                return []
            
            recent_data = []
            for timestamp, data in self.time_window_data[room_id]:
                if timestamp > since_timestamp:
                    recent_data.append(data)
                    if limit and len(recent_data) >= limit:
                        break
            
            if recent_data:
                self.metrics.cache_hits += 1
                logger.debug(f"获取到 {len(recent_data)} 条时间窗口数据 - 房间: {room_id}")
            else:
                self.metrics.cache_misses += 1
                logger.debug(f"时间窗口数据为空 - 房间: {room_id}")
            
            # 记录响应时间
            response_time = time.time() - start_time
            self._update_avg_response_time(response_time)
            
            return recent_data
    
    def get_version(self, room_id: str) -> int:
        """获取房间数据版本号
        
        Args:
            room_id: 房间ID
            
        Returns:
            版本号
        """
        with self._lock:
            return self.data_versions.get(room_id, 0)
    
    def check_version_update(self, room_id: str, client_version: int) -> bool:
        """检查是否有新版本数据
        
        Args:
            room_id: 房间ID
            client_version: 客户端版本号
            
        Returns:
            是否有更新
        """
        current_version = self.get_version(room_id)
        return current_version > client_version
    
    def get_room_info(self, room_id: str) -> Dict[str, Any]:
        """获取房间信息
        
        Args:
            room_id: 房间ID
            
        Returns:
            房间信息
        """
        with self._lock:
            has_data = room_id in self.latest_data
            version = self.data_versions.get(room_id, 0)
            window_size = len(self.time_window_data.get(room_id, []))
            expire_time = self.cache_expiry.get(room_id, 0)
            
            return {
                'room_id': room_id,
                'has_data': has_data,
                'version': version,
                'window_size': window_size,
                'expire_time': expire_time,
                'is_expired': time.time() > expire_time if expire_time > 0 else False
            }
    
    def cleanup_expired_data(self) -> int:
        """清理过期数据
        
        Returns:
            清理的数据条数
        """
        start_time = time.time()
        cleaned_count = 0
        
        with self._lock:
            current_time = time.time()
            
            # 清理过期的房间缓存
            expired_rooms = []
            for room_id, expire_time in self.cache_expiry.items():
                if current_time > expire_time:
                    expired_rooms.append(room_id)
            
            for room_id in expired_rooms:
                self._remove_room_cache(room_id)
                cleaned_count += 1
            
            # 清理时间窗口数据（保留最近5分钟）
            window_seconds = 300
            cutoff_time = current_time - window_seconds
            
            for room_id, data_queue in self.time_window_data.items():
                while data_queue and data_queue[0][0] < cutoff_time:
                    data_queue.popleft()
                    cleaned_count += 1
            
            self.metrics.cleanup_runs += 1
            
            cleanup_time = time.time() - start_time
            logger.info(f"清理完成 - 清理了 {cleaned_count} 条数据，耗时 {cleanup_time:.3f}s")
            
            return cleaned_count
    
    def get_cache_stats(self) -> Dict[str, Any]:
        """获取缓存统计信息
        
        Returns:
            统计信息字典
        """
        with self._lock:
            total_window_data = sum(len(queue) for queue in self.time_window_data.values())
            
            return {
                'total_rooms': len(self.latest_data),
                'total_window_data': total_window_data,
                'metrics': asdict(self.metrics),
                'memory_usage': self._get_memory_usage(),
                'config': {
                    'max_rooms': self.max_rooms,
                    'max_window_size': self.max_window_size,
                    'cache_ttl': self.cache_ttl,
                    'cleanup_interval': self.cleanup_interval
                }
            }
    
    def clear_room_cache(self, room_id: str) -> bool:
        """清除指定房间的缓存
        
        Args:
            room_id: 房间ID
            
        Returns:
            是否成功清除
        """
        with self._lock:
            if room_id in self.latest_data:
                self._remove_room_cache(room_id)
                logger.info(f"已清除房间缓存 - 房间: {room_id}")
                return True
            return False
    
    def clear_all_cache(self) -> int:
        """清除所有缓存
        
        Returns:
            清除的房间数量
        """
        with self._lock:
            room_count = len(self.latest_data)
            
            self.latest_data.clear()
            self.data_versions.clear()
            self.time_window_data.clear()
            self.cache_expiry.clear()
            
            # 重置指标
            self.metrics = CacheMetrics()
            
            logger.info(f"已清除所有缓存 - 共 {room_count} 个房间")
            return room_count
    
    def _evict_oldest_room(self) -> None:
        """驱逐最旧的房间数据"""
        if not self.cache_expiry:
            return
        
        oldest_room = min(self.cache_expiry.keys(), 
                         key=lambda x: self.cache_expiry[x])
        self._remove_room_cache(oldest_room)
        logger.debug(f"驱逐最旧房间 - 房间: {oldest_room}")
    
    def _remove_room_cache(self, room_id: str) -> None:
        """移除房间缓存"""
        self.latest_data.pop(room_id, None)
        self.data_versions.pop(room_id, None)
        self.time_window_data.pop(room_id, None)
        self.cache_expiry.pop(room_id, None)
    
    def _update_avg_response_time(self, response_time: float) -> None:
        """更新平均响应时间"""
        if self.metrics.total_requests == 0:
            self.metrics.avg_response_time = response_time
        else:
            # 使用指数移动平均
            alpha = 0.1
            self.metrics.avg_response_time = (
                alpha * response_time + 
                (1 - alpha) * self.metrics.avg_response_time
            )
    
    def _get_memory_usage(self) -> MemoryUsage:
        """获取内存使用情况"""
        try:
            process = psutil.Process()
            memory_info = process.memory_info()
            
            return MemoryUsage(
                rss_mb=memory_info.rss / 1024 / 1024,
                vms_mb=memory_info.vms / 1024 / 1024,
                percent=process.memory_percent(),
                cache_size=len(self.latest_data)
            )
        except Exception as e:
            logger.warning(f"获取内存使用情况失败: {e}")
            return MemoryUsage(0, 0, 0, len(self.latest_data))
    
    def _start_cleanup_timer(self) -> None:
        """启动清理定时器"""
        def cleanup_task():
            try:
                self.cleanup_expired_data()
            except Exception as e:
                logger.error(f"清理任务执行失败: {e}")
            finally:
                # 重新启动定时器
                self._start_cleanup_timer()
        
        self._cleanup_timer = threading.Timer(self.cleanup_interval, cleanup_task)
        self._cleanup_timer.daemon = True
        self._cleanup_timer.start()
    
    def shutdown(self) -> None:
        """关闭缓存服务"""
        if self._cleanup_timer:
            self._cleanup_timer.cancel()
        
        with self._lock:
            room_count = len(self.latest_data)
            self.clear_all_cache()
        
        logger.info(f"内存缓存服务已关闭 - 清理了 {room_count} 个房间的数据")
    
    def __del__(self):
        """析构函数"""
        try:
            self.shutdown()
        except:
            pass


# 全局缓存实例
_global_cache_instance: Optional[MemoryCacheService] = None
_cache_lock = threading.Lock()


def get_memory_cache() -> MemoryCacheService:
    """获取全局内存缓存实例（单例模式）
    
    Returns:
        内存缓存服务实例
    """
    global _global_cache_instance
    
    if _global_cache_instance is None:
        with _cache_lock:
            if _global_cache_instance is None:
                _global_cache_instance = MemoryCacheService()
    
    return _global_cache_instance


def shutdown_memory_cache() -> None:
    """关闭全局内存缓存实例"""
    global _global_cache_instance
    
    if _global_cache_instance is not None:
        with _cache_lock:
            if _global_cache_instance is not None:
                _global_cache_instance.shutdown()
                _global_cache_instance = None